package service

import (
	"errors"
	"fmt"
	"ginstudydemo.com/main/models"
	"github.com/golang-jwt/jwt/v5"
	"time"
)

//const TokenExpireDuration = time.Hour * 2

var MySecret = []byte("yoursecret") // 生成签名的密钥

type MyClaims struct {
	// UserInfo 是传入的用户信息，将其包含在 JWT 中。
	UserInfo             models.SysUserUsersLoginTokenUserInfo
	jwt.RegisteredClaims // 标准的 JWT 声明, 标准Claims结构体，可设置8个标准字段
}

type JwtTokens struct {
	AccessToken string `json:"accessToken"` // 验证token
}

type IJwtV5 interface {
	EncryptUserToken()
	DecodeUserToken()
}

type JwtV5 struct{}

func NewJwtV5() *JwtV5 {
	return &JwtV5{}
}

// EncryptUserToken 生成一个带有自定义声明（claims）的 JWT（JSON Web Token）令牌。
func (j JwtV5) EncryptUserToken(userInfo models.SysUserUsersLoginTokenUserInfo) (string, error) {

	// 自定义声明（claims）
	claims := &MyClaims{
		UserInfo: userInfo,
		RegisteredClaims: jwt.RegisteredClaims{
			NotBefore: jwt.NewNumericDate(time.Now()),                       // 令牌生成立即生效
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(10 * time.Minute)), // 访问令牌有效期为 15 分钟（ TODO：测试改为1分钟）
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "dys6230",
		},
	}
	// 创建和签名 JWT 令牌,创建了一个新的 JWT 令牌，使用 HS256 算法（HMAC SHA-256）对令牌进行签名。令牌中包含前面定义的 claims，即用户信息和声明。
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// 令牌通过 HMAC SHA-256 算法签名，并返回生成的 JWT 字符串。MySecret 是用于 HMAC SHA-256 签名的密钥.
	if tokenString, err := token.SignedString(MySecret); err != nil {
		return "", err
	} else {
		return tokenString, nil
	}

}

// EncryptRefreshToken 生成一个用于刷新 JWT 的 Refresh Token。
func (j JwtV5) EncryptRefreshToken(userInfo models.SysUserUsersLoginTokenUserInfo) (string, error) {
	// 自定义声明（claims）
	claims := &MyClaims{
		UserInfo: userInfo,
		RegisteredClaims: jwt.RegisteredClaims{
			NotBefore: jwt.NewNumericDate(time.Now()),                      // 令牌生成立即生效
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Minute)), // 刷新令牌的过期时间（7天） 24 * time.Hour * 7
			IssuedAt:  jwt.NewNumericDate(time.Now()),                      // 令牌生成时间
			Issuer:    "dys6230",                                           // 发行者
		},
	}

	// 创建和签名 Refresh Token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 签名并返回刷新令牌字符串
	if tokenString, err := token.SignedString(MySecret); err != nil {
		return "", err
	} else {
		return tokenString, nil
	}
}

// DecodeUserToken 验证token
func (j JwtV5) DecodeUserToken(tokenString string) (*MyClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(t *jwt.Token) (interface{}, error) {
		return MySecret, nil
	})

	if err != nil {
		return nil, err
	}
	claims, ok := token.Claims.(*MyClaims)
	//if !ok || !token.Valid {
	if !ok {
		return nil, errors.New("unknown claims type, cannot proceed")
	}

	return claims, nil

	//以下是续签逻辑，后期考虑实现
	//if ok {
	//	fmt.Println(claims.User, claims.RegisteredClaims.Issuer)
	//} else {
	//	log.Fatal("unknown claims type, cannot proceed")
	//}
	//
	//claims, ok := token.Claims.(*MyClaims)
	//if ok && token.Valid {
	//	//fmt.Printf("%v %v", claims.User, claims.StandardClaims.ExpiresAt)
	//	return claims, nil
	//} else {
	//	//fmt.Println(err)
	//	return claims, err
	//}

}

// RenewToken 颁发新token，续签
func (j JwtV5) RenewToken(claims *MyClaims) (string, error) {
	fmt.Println(claims)
	//TODO:续签之前应该查一下用户是否仍在数据库中
	// 若token过期不超过10分钟则给它续签
	if withinLimit(claims.ExpiresAt.Unix(), 600) {
		return NewJwtV5().EncryptUserToken(claims.UserInfo)
	}
	return "", errors.New("登录已过期")
}

// 计算过期时间是否超过l
func withinLimit(s int64, l int64) bool {
	e := time.Now().Unix()
	return e-s < l
}
